﻿using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace YWM.Communication
{
    public class TCPUtil : CommunicationUtil
    {
        private Socket _socket;

        /// <summary>
        /// TCP是否连接
        /// </summary>
        public override bool Connected
        {
            get
            {
                return _socket != null && _socket.Connected;
            }
        }

        /// <summary>
        /// 打开TCP连接
        /// </summary>
        /// <param name="ip">IP地址</param>
        /// <param name="port">端口号</param>
        /// <returns></returns>
        public override (bool, Exception) Connect(string ip, int port)
        {
            try
            {
                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _socket.SendTimeout = SendTimeout;
                _socket.ReceiveTimeout = ReceiveTimeout;
                _socket.Connect(IPAddress.Parse(ip), port);
                return (true, null);
            }
            catch (Exception ex)
            {
                return (false, ex);
            }
        }

        /// <summary>
        /// 异步实现TCP发送并接收
        /// </summary>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public override async Task<(byte[], Exception)> SendAndReceiveAsync(byte[] buffer)
        {
            using (await _mutex.LockAsync())
            {
                (byte[], Exception) tuple = await Task.Factory.StartNew(state =>
                {
                    byte[] data = null;
                    Exception exception = null;
                    try
                    {
                        byte[] sendBuffer = (byte[])state;
                        _socket.Send(sendBuffer, 0, sendBuffer.Length, SocketFlags.None);
                        int receiveTime = 0;
                        do
                        {
                            Task.Delay(Delay).Wait();
                            byte[] reveiveBuffer = new byte[_socket.Available];
                            int count = _socket.Receive(reveiveBuffer, 0, reveiveBuffer.Length, SocketFlags.None);
                            if (count > 0)
                            {
                                MemoryStream stream = new MemoryStream();
                                stream.Write(reveiveBuffer, 0, count);
                                data = stream.ToArray();
                                break;
                            }
                            receiveTime += Delay;
                        } while (ReceiveTimeout > receiveTime);
                    }
                    catch (Exception ex)
                    {
                        exception = ex;
                    }
                    return (data, exception);
                }, buffer);
                return tuple;
            }
        }

        public override void Disconnect()
        {
            if (Connected)
            {
                _socket.Disconnect(false);
            }
        }
    }
}
